﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;

namespace csis.ImmediatorClasses
{
	/// <summary>
	/// Preprocesses a single script.
	/// </summary>
	public class Preprocessor
	{
		public Preprocessor(Script script)
		{
			if (script == null) throw new ArgumentNullException("script");

			this.script = script;
		}

		private readonly Script script;

		private readonly List<string> includedFiles = new List<string>();
		private readonly List<string> referencedFiles = new List<string>();

		private List<string> errors;

		public IEnumerable<string> Errors { get { return this.errors; } }
		public IEnumerable<string> IncludedFiles { get { return this.includedFiles; } }
		public IEnumerable<string> ReferencedAssemblies { get { return this.referencedFiles; } }

		public bool ScriptHelpersRequested { get; private set; }

		private void AddError(string message)
		{
			if (this.errors == null)
			{
				this.errors = new List<string>();
			}
			this.errors.Add(message);
		}

		public static Preprocessor Run(Script script)
		{
			var preprocessor = new Preprocessor(script);
			preprocessor.Run();
			return preprocessor;
		}

		public void Run()
		{
			var lines = File.ReadAllLines(this.script.FilePath);

			for (int i = 0; i < lines.Length; ++i)
			{
				var line = lines[i];
				var match = Regex.Match(line, @"^\s*///\s*csis\s+(?<command>.+)");
				if (match.Success)
				{
					var command = match.Groups["command"].Value;
					this.Evaluate(command, i);
				}
			}

			ScriptTransformer.TransformNew(this.script, lines);
		}

		private void Evaluate(string command, int lineIndex)
		{
			var parts = command.Split(new [] { ' ' }, 2);
			if (parts[0] == "include")
			{
				if (parts.Length != 2)
				{
					this.AddError("No file specified for 'include'");
					return;
				}
				this.Include(parts[1]);
			}
			else if (parts[0] == "reference")
			{
				if (parts.Length != 2)
				{
					this.AddError("No file specified for 'reference'");
					return;
				}
				this.Reference(parts[1]);
			}
			else if (parts[0] == "qk")
			{
				this.QuickHeader();
			}
			else if (parts[0] == "automain")
			{
				this.AutoMain(lineIndex);
			}
			else if (parts[0] == "scripthelpers")
			{
				this.ScriptHelpers();
			}
			else
			{
				this.AddError("Unknown command: '" + parts[0] + "'");
			}
		}

		private void AutoMain(int line)
		{
			this.script.AutoMain = line;
		}

		private void QuickHeader()
		{
			this.script.UseQuickHeader = true;
			this.Reference("System.dll");
			this.Reference("System.Core.dll");
		}

		private void Include(string file)
		{
			this.includedFiles.Add(file);
		}

		private void Reference(string file)
		{
			this.referencedFiles.Add(file);
		}

		private void ScriptHelpers()
		{
			this.ScriptHelpersRequested = true;
		}
	}
}
